package com.tools.web.filter.cors;

import com.tools.common.exception.InvalidOperationException;
import com.tools.common.object.Note;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * 自定义的跨域过滤器
 * */
@Note("自定义的跨域过滤器")
public final class CorsFilter implements Filter {

    @Note("允许哪些域名能请求")
    private final String allowOrigin;

    @Note("允许哪些请求方法。多个以逗号分隔")
    private final String allowMethods;

    @Note("允许携带哪些请求头。多个以逗号分隔")
    private final String allowHeaders;

    @Note("设置是否允许浏览器携带 Cookie 等标识")
    private final boolean allowCredentials;

    @Note("设置预检请求的缓存结果有效时长，默认是 30 分钟（1800 秒）")
    private final int maxAge;

    /* ********************************************************************************************
     *
     *         构造器
     *
     * *********************************************************************************************
     * */

    public CorsFilter(CorsSource source) {
        if(source == null) throw new NullPointerException("跨域过滤器配置源为 null");
        this.allowOrigin = source.getAllowOrigin();
        this.allowCredentials = source.isAllowCredentials();
        if(this.allowCredentials && "*".equals(this.allowOrigin)) {
            throw new InvalidOperationException("不能设置 Access-Control-Allow-Origin 为 * 的同时，" +
                    "设置 Access-Control-Allow-Credentials 为 true。" +
                    "当前端设定了要发送 cookie 等数据时必须明确 Allow-Origin 为指定的 URL");
        }
        this.allowMethods = source.getAllowMethods();
        this.allowHeaders = source.getAllowHeaders();
        this.maxAge = source.getMaxAge();
    }

    /* ********************************************************************************************
     *
     *         核心过滤逻辑
     *
     * *********************************************************************************************
     * */

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest) servletRequest;
        HttpServletResponse response = (HttpServletResponse) servletResponse;
        if(this.preFiltering(request, response)) {
            return;
        }
        filterChain.doFilter(request, response);
    }


    @Note("核心配置")
    public boolean preFiltering(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        response.setHeader("Access-Control-Allow-Origin", this.allowOrigin);
        response.setHeader("Access-Control-Allow-Methods", this.allowMethods);
        response.setHeader("Access-Control-Allow-Headers", this.allowHeaders);
        response.setHeader("Access-Control-Allow-Credentials", String.valueOf(this.allowCredentials));
        response.setHeader("Access-Control-Max-Age", String.valueOf(this.maxAge));
        // 如果是 OPTIONS 预检请求，直接返回状态码 200
        if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
            response.setStatus(HttpServletResponse.SC_OK);
            return true;
        }
        return false;
    }
}
